package com.vegetable.modules.service.allocation.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vegetable.common.constant.CommonConstant;
import com.vegetable.common.utils.R;
import com.vegetable.modules.entity.allocation.AllocationCollection;
import com.vegetable.modules.entity.allocation.AllocationCollectionDetail;
import com.vegetable.modules.entity.allocation.AllocationDeductionDetail;
import com.vegetable.modules.entity.allocation.po.*;
import com.vegetable.modules.entity.allocation.vo.AllocationCollectionDetailWithSalesInfo;
import com.vegetable.modules.entity.allocation.vo.DeductionDetailVO;
import com.vegetable.modules.entity.allocation.vo.DeductionProcessResultVO;
import com.vegetable.modules.entity.allocation.vo.DeductionStatisticsVO;
import com.vegetable.modules.entity.allocation.vo.SalesOrderCollectionVO;
import com.vegetable.modules.entity.outbound.SalesOrder;
import com.vegetable.modules.entity.outbound.SalesOrderDetail;
import com.vegetable.modules.entity.outbound.SalesOrderDetailStock;
import com.vegetable.modules.entity.outbound.SalesOut;
import com.vegetable.modules.entity.stock.Stock;
import com.vegetable.modules.entity.sys.SysMessageEntity;
import com.vegetable.modules.entity.sys.SysUserEntity;
import com.vegetable.modules.mapper.allocation.AllocationCollectionDetailMapper;
import com.vegetable.modules.mapper.allocation.AllocationCollectionMapper;
import com.vegetable.modules.mapper.allocation.AllocationDeductionDetailMapper;
import com.vegetable.modules.service.allocation.IAllocationCollectionService;
import com.vegetable.modules.service.outbound.ISalesOrderDetailService;
import com.vegetable.modules.service.outbound.ISalesOrderDetailStockService;
import com.vegetable.modules.service.outbound.ISalesOrderService;
import com.vegetable.modules.service.outbound.ISalesOutService;
import com.vegetable.modules.service.stock.IStockService;
import com.vegetable.common.utils.MessageUtils;
import com.vegetable.modules.service.sys.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 分配回款主表 服务实现类
 * </p>
 *
 * @author Qoder
 * @since 2025-10-08
 */
@Slf4j
@Service
public class AllocationCollectionServiceImpl extends ServiceImpl<AllocationCollectionMapper, AllocationCollection> implements IAllocationCollectionService {

    @Autowired
    private SysUserService sysUserService;
    
    @Autowired
    private AllocationCollectionDetailMapper allocationCollectionDetailMapper;
    
    @Autowired
    private AllocationDeductionDetailMapper allocationDeductionDetailMapper;
    
    @Autowired
    private ISalesOutService salesOutService;
    
    @Autowired
    private ISalesOrderService salesOrderService;
    
    @Autowired
    private ISalesOrderDetailService salesOrderDetailService;
    
    @Autowired
    private ISalesOrderDetailStockService salesOrderDetailStockService;
    
    @Autowired
    private IStockService stockService;

    /**
     * 分页查询分配回款列表
     * @param queryDTO 查询参数DTO
     * @return 分页结果
     */
    @Override
    public IPage<AllocationCollection> queryPage(AllocationCollectionQueryDTO queryDTO) {
        Page<AllocationCollection> page = new Page<>(queryDTO.getPage(), queryDTO.getLimit());
        IPage<AllocationCollection> result = baseMapper.selectPageWithConditions(page, queryDTO);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R saveAllocationCollection(AllocationCollectionAddPO po) throws Exception {
        // 检查是否已存在相同transaction_id的记录
        QueryWrapper<AllocationCollection> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("transaction_id", po.getTransactionId());
        AllocationCollection existingRecord = this.getOne(queryWrapper);
        
        AllocationCollection order;
        if (existingRecord != null) {
            // 如果存在，则更新现有记录
            order = existingRecord;
            BeanUtils.copyProperties(po, order, "id", "docNo"); // 保留原有的id和docNo
        } else {
            // 如果不存在，则创建新记录
            order = new AllocationCollection();
            BeanUtils.copyProperties(po, order);
            
            if (order.getId() == null){
                order.setDocNo(CommonConstant.FK+ baseMapper.getNextCode());
            }
        }
        
        // 计算已分配金额
        BigDecimal allocatedAmount = BigDecimal.ZERO;
        if (CollectionUtil.isNotEmpty(po.getDetailList())) {
            allocatedAmount = po.getDetailList().stream()
                    .map(AllocationCollectionDetailPO::getAllocatedAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
        }
        
        // 计算未分配金额
        order.setAllocatedAmount(allocatedAmount);
        order.setUnallocatedAmount(NumberUtil.sub(po.getTotalAmount(), allocatedAmount));
        
        // 更新扣款状态字段
        updateDeductionStatus(order, po.getDetailList());
        
        this.saveOrUpdate(order);
        return R.data(order.getId());
    }

    /**
     * 更新扣款状态字段
     * @param order 分配回款主表记录
     * @param detailList 明细列表
     */
    private void updateDeductionStatus(AllocationCollection order, List<AllocationCollectionDetailPO> detailList) {
        if (CollectionUtil.isNotEmpty(detailList)) {
            // 检查明细中是否有扣款金额大于0的记录
            boolean hasDeduction = detailList.stream()
                    .anyMatch(detail -> detail.getDeductionAmount() != null && 
                             NumberUtil.isGreater(detail.getDeductionAmount(), BigDecimal.ZERO));
            order.setDeductionStatus(hasDeduction ? 1 : 0);
        } else {
            order.setDeductionStatus(0);
        }
    }

    /**
     * 分配回款
     * @param po 分配回款添加PO
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R allocatedCollection(AllocationCollectionAddPO po) throws Exception {
        List<AllocationCollectionDetailPO> detailList = po.getDetailList();
        if(CollectionUtil.isEmpty(detailList)){
            throw new Exception("回款分配不能为空");
        }
        
        log.info("开始保存回款明细，共:{}条", detailList.size());
        
        AllocationCollection order = getById(po.getId());
        if(order == null){
            throw new Exception("回款记录不存在，请检查后重试");
        }
        
        // 保存明细
        List<AllocationCollectionDetail> details = new ArrayList<>();
        BigDecimal allocatedAmount = BigDecimal.ZERO;
        
        for (AllocationCollectionDetailPO detailPO : detailList) {
            AllocationCollectionDetail detail = new AllocationCollectionDetail();
            BeanUtils.copyProperties(detailPO, detail);
            detail.setMainId(po.getId());
            details.add(detail);
            allocatedAmount = allocatedAmount.add(detailPO.getAllocatedAmount());
        }
        
        // 批量保存明细
        if (CollectionUtil.isNotEmpty(details)) {
            // 先删除原有明细
            allocationCollectionDetailMapper.delete(new QueryWrapper<AllocationCollectionDetail>().eq("main_id", po.getId()));
            // 保存新明细
            details.forEach(detail -> allocationCollectionDetailMapper.insert(detail));
        }
        
        // 处理扣款逻辑
        if (po.getHasDeduction() != null && po.getHasDeduction() == 1) {
            // 保存扣款记录
            saveDeductionRecords(po, detailList);
            
            // 发送系统消息给指定用户 f005 和 f009
            sendDeductionNotification(order, detailList);
        }
        
        // 更新主表已分配金额和未分配金额
        order.setAllocatedAmount(allocatedAmount);
        order.setUnallocatedAmount(NumberUtil.sub(order.getTotalAmount(), allocatedAmount));
        
        // 更新扣款状态字段
        updateDeductionStatus(order, detailList);
        
        // 注意：分配时不需要更新销售出库单的对冲状态（回款完成状态）
        // collectionCompleteStatus这个字段表示这笔回款是否分配完毕，而不是回款完成状态
        // 回款完成状态应该在销售出库单上通过hedging_state字段来表示
        
        updateById(order);
        
        // 同步更新相关销售出库单的扣款金额和已回款金额
        if (CollectionUtil.isNotEmpty(details)) {
            syncSalesOutAmounts(details);
        }
        
        return R.data(order.getId());
    }

    /**
     * 保存扣款记录
     * @param po 分配回款添加PO
     * @param detailList 明细列表
     */
    private void saveDeductionRecords(AllocationCollectionAddPO po, List<AllocationCollectionDetailPO> detailList) {
        // 移除对allocation_deduction表的操作，因为扣款信息已存储在allocation_collection_detail表中
        // 这里可以保留方法签名以保持接口一致性，但方法体可以为空
    }

    /**
     * 发送扣款通知给指定用户
     * @param collection 回款记录
     * @param detailList 明细列表
     */
    private void sendDeductionNotification(AllocationCollection collection, List<AllocationCollectionDetailPO> detailList) {
        try {
            // 查找需要通知的用户 f005 和 f009
            List<SysUserEntity> users = sysUserService.list(new QueryWrapper<SysUserEntity>().in("username", "f005", "f009"));
            
            if (CollectionUtil.isNotEmpty(users)) {
                List<SysMessageEntity> messages = new ArrayList<>();
                
                // 构建扣款详情信息
                StringBuilder deductionDetails = new StringBuilder();
                deductionDetails.append("回款单[").append(collection.getDocNo()).append("]存在扣款，具体扣款明细如下：\n");
                
                for (AllocationCollectionDetailPO detailPO : detailList) {
                    if (detailPO.getDeductionAmount() != null && NumberUtil.isGreater(detailPO.getDeductionAmount(), BigDecimal.ZERO)) {
                        // 查询销售出库单信息
                        SalesOut salesOut = salesOutService.getById(detailPO.getSalesOutId());
                        String salesOutDocNo = salesOut != null ? salesOut.getDocNo() : "未知出库单";
                        
                        deductionDetails.append("销售出库单[").append(salesOutDocNo).append("]，扣款金额：")
                                .append(detailPO.getDeductionAmount()).append("，扣款原因：")
                                .append(detailPO.getDeductionReason()).append("\n");
                    }
                }

                for (SysUserEntity user : users) {
                    SysMessageEntity message = new SysMessageEntity();
                    message.setReceiverUserId(user.getUserId());
                    message.setTitle("回款扣款通知");
                    message.setContent(deductionDetails.toString());
                    message.setType(1); // 系统消息
                    message.setImportant(1); // 重要消息
                    messages.add(message);
                }
                
                // 批量发送消息
                MessageUtils.sendBatchMessage(messages);
            }
        } catch (Exception e) {
            log.error("发送扣款通知失败：{}", e.getMessage(), e);
        }
    }

    @Override
    public R detailCollection(Long id) {
        AllocationCollection collection = baseMapper.selectById(id);
        if (collection == null){
            return R.fail("订单不存在");
        }
        
        // 通过优化的SQL查询直接获取包含销售信息的明细列表
        List<AllocationCollectionDetailWithSalesInfo> detailWithSalesInfoList = 
            baseMapper.selectCollectionDetailWithSalesInfo(id);
        
        // 将扩展的明细列表转换为原始的明细列表，保持接口返回结构不变
        List<AllocationCollectionDetail> detailList = new ArrayList<>();
        for (AllocationCollectionDetailWithSalesInfo detailWithSalesInfo : detailWithSalesInfoList) {
            detailList.add(detailWithSalesInfo);
        }
        
        // 将明细列表设置到主表对象中
        collection.setDetailList(detailList);
        
        return R.data(collection);
    }

    /**
     * 更新回款完成状态
     * 注意：这里只更新分配回款主表的状态，不直接更新销售出库单的状态
     * 销售出库单的回款完成状态应该通过其他方式更新
     * @param id 回款记录ID
     * @param status 状态 0未完成，1已完成
     * @return 结果
     */
    @Override
    public R updateCollectionCompleteStatus(Long id, Integer status) {
        AllocationCollection collection = getById(id);
        if (collection == null) {
            return R.fail("回款记录不存在");
        }
        collection.setCollectionCompleteStatus(status);
        updateById(collection);
        
        // 注意：这里不再更新销售出库单的对冲状态
        // 销售出库单的回款完成状态应该通过其他业务逻辑来更新
        
        return R.success("状态更新成功");
    }

    /**
     * 删除分配回款的明细数据
     * 注意：不删除主表数据，因为主表数据是由财务流水产生的，只能删除分配给每个订单的明细数据
     * @param id 主键ID
     * @return 结果
     */
    @Override
    @Transactional
    public R deleteCollection(Long id) throws Exception {
        log.info("根据 {} 查找回款明细:" , id);
        AllocationCollectionDetail currentT = allocationCollectionDetailMapper.selectById(id);
        if(Objects.isNull(currentT)){
            throw new RuntimeException("要删除的明细不存在");
        }

        // 删除回款明细
        allocationCollectionDetailMapper.deleteById(id);
        BigDecimal totalAllocatedAmount = BigDecimal.ZERO;
        List<AllocationCollectionDetail> exists = allocationCollectionDetailMapper.selectList(new QueryWrapper<AllocationCollectionDetail>().eq("main_id", currentT.getMainId()));
        if(CollectionUtil.isNotEmpty(exists)){
            totalAllocatedAmount = exists.stream().map(AllocationCollectionDetail::getAllocatedAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        }
        AllocationCollection collection = baseMapper.selectById(currentT.getMainId());
        if(Objects.isNull(collection)){
            log.info("通过mainId {} 没有找到汇款记录", currentT.getMainId());
            return R.status(true);
        }
        // 重置主表的分配金额和未分配金额
        collection.setAllocatedAmount(totalAllocatedAmount);
        collection.setUnallocatedAmount(NumberUtil.sub(collection.getTotalAmount(), totalAllocatedAmount));
        updateById(collection);
        // 同步更新相关销售出库单的扣款金额和已回款金额
        syncSalesOutAmounts(Collections.singletonList(currentT));
        return R.status(true);
    }

    /**
     * 同步更新相关销售出库单的扣款金额和已回款金额
     * @param details 分配回款明细列表
     */
    private void syncSalesOutAmounts(List<AllocationCollectionDetail> details) {
        try {
            // 按销售出库单ID分组处理
            Map<Long, List<AllocationCollectionDetail>> salesOutDetailsMap = details.stream()
                    .collect(Collectors.groupingBy(AllocationCollectionDetail::getSalesOutId));
            List<Long> salesOutIds = details.stream().map(AllocationCollectionDetail::getSalesOutId).collect(Collectors.toList());
            
            for (Long salesOutId : salesOutIds) {
                // 查询销售出库单
                SalesOut salesOut = salesOutService.getById(salesOutId);
                if (salesOut != null) {
                    QueryWrapper<AllocationCollectionDetail> wrapper = new QueryWrapper<>();
                    wrapper.eq("sales_out_id", salesOutId);
                    List<AllocationCollectionDetail> salesOutDetails = allocationCollectionDetailMapper.selectList(wrapper);
                    // 计算该销售出库单的总扣款金额和总回款金额
                    BigDecimal totalDeductionAmount = salesOutDetails.stream()
                            .map(AllocationCollectionDetail::getDeductionAmount)
                            .filter(Objects::nonNull)
                            .reduce(BigDecimal.ZERO, BigDecimal::add);

                    BigDecimal totalAllocatedAmount = salesOutDetails.stream()
                            .map(AllocationCollectionDetail::getAllocatedAmount)
                            .filter(Objects::nonNull)
                            .reduce(BigDecimal.ZERO, BigDecimal::add);
                    
                    // 更新扣款金额和已回款金额
                    salesOut.setDeductionMoney(totalDeductionAmount);
                    salesOut.setReceivedMoney(totalAllocatedAmount);
                    
                    // 保存更新
                    salesOutService.updateById(salesOut);
                }
            }
        } catch (Exception e) {
            log.error("同步更新销售出库单金额失败：{}", e.getMessage(), e);
        }
    }
    
    /**
     * 统计扣款情况
     * @param queryDTO 查询参数DTO
     * @return 扣款统计列表
     */
    @Override
    public List<DeductionStatisticsVO> getDeductionStatistics(DeductionStatisticsQueryDTO queryDTO) {
        // 修改查询逻辑，直接从allocation_collection_detail表查询
        return allocationCollectionDetailMapper.selectDeductionStatistics(queryDTO);
    }
    
    /**
     * 根据条件查询回款情况
     * @param queryDTO 查询条件
     * @return 回款情况列表
     */
    @Override
    public List<SalesOrderCollectionVO> getCollectionDetailsByConditions(CollectionDetailsQueryDTO queryDTO) {
        return baseMapper.selectCollectionDetailsByConditions(
            queryDTO.getCustomerId(),
            queryDTO.getOrderNo(),
            queryDTO.getStartTime(),
            queryDTO.getEndTime()
        );
    }
    
    /**
     * 同时更新扣款处理状态和处理意见（包含明细）
     * @param po 更新扣款处理意见参数对象
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R updateDeductionHandleStatusAndOpinionWithDetails(UpdateDeductionHandleOpinionWithDetailsPO po) {
        if (po.getId() == null || po.getId() <= 0) {
            return R.fail("记录ID不能为空");
        }
        
        if (po.getStatus() == null || (po.getStatus() != 0 && po.getStatus() != 1)) {
            return R.fail("状态值不正确，应为0或1");
        }
        
        // 查询记录是否存在
        AllocationCollectionDetail detail = allocationCollectionDetailMapper.selectById(po.getId());
        if (detail == null) {
            return R.fail("记录不存在");
        }
        
        // 更新扣款处理状态和处理意见
        detail.setDeductionHandleStatus(po.getStatus());
        detail.setDeductionHandleOpinion(po.getOpinion());
        allocationCollectionDetailMapper.updateById(detail);
        
        // 保存扣款明细记录到数据库
        if (CollectionUtil.isNotEmpty(po.getAgentDeductions())) {
            // 先删除原有的扣款明细记录
            allocationDeductionDetailMapper.delete(new QueryWrapper<AllocationDeductionDetail>().eq("collection_detail_id", po.getId()));
            
            // 保存新的扣款明细记录
            for (DeductionDetailVO deductionDetailVO : po.getAgentDeductions()) {
                AllocationDeductionDetail deductionDetail = new AllocationDeductionDetail();
                deductionDetail.setCollectionDetailId(po.getId());
                deductionDetail.setAgentId(deductionDetailVO.getAgentId());
                deductionDetail.setAgentName(deductionDetailVO.getAgentName());
                deductionDetail.setCommodityId(deductionDetailVO.getCommodityId());
                deductionDetail.setCommodityName(deductionDetailVO.getCommodityName());
                deductionDetail.setSpecsId(deductionDetailVO.getSpecsId());
                deductionDetail.setSpecsName(deductionDetailVO.getSpecsName());
                deductionDetail.setDeductionAmount(deductionDetailVO.getDeductionAmount());
                deductionDetail.setSalesOutId(deductionDetailVO.getSalesOutId());
                deductionDetail.setSalesOutDocNo(deductionDetailVO.getSalesOutDocNo());
                deductionDetail.setOrderTime(deductionDetailVO.getOrderTime());
                deductionDetail.setStorageOrderNo(deductionDetailVO.getStorageOrderNo());
                deductionDetail.setRemark(deductionDetailVO.getRemark());
                allocationDeductionDetailMapper.insert(deductionDetail);
            }
        }
        
        if (po.getCompanyDeductionAmount() != null) {
            // 保存企业承担金额到分配回款明细中
            detail.setCompanyDeductionAmount(po.getCompanyDeductionAmount());
            allocationCollectionDetailMapper.updateById(detail);
        }
        
        return R.success("更新成功");
    }
    
    /**
     * 根据销售出库单ID获取走货详情用于扣款处理
     * @param salesOutId 销售出库单ID
     * @return 扣款处理结果VO
     */
    @Override
    public DeductionProcessResultVO getShipmentDetailsForDeduction(Long salesOutId) {
        DeductionProcessResultVO result = new DeductionProcessResultVO();
        
        // 查询销售出库单
        SalesOut salesOut = salesOutService.getById(salesOutId);
        if (salesOut == null) {
            return result;
        }
        
        result.setSalesOutId(salesOutId);
        result.setSalesOutDocNo(salesOut.getDocNo());
        
        // 查询销售订单
        SalesOrder salesOrder = salesOrderService.getById(salesOut.getSalesOrderId());
        
        // 查询销售出库单明细
        List<SalesOrderDetail> orderDetails = salesOrderDetailService.selectByOrderId(salesOut.getSalesOrderId());
        
        List<DeductionDetailVO> deductionDetails = new ArrayList<>();
        BigDecimal totalDeductionAmount = BigDecimal.ZERO;
        
        // 遍历销售订单明细，获取走货详情
        for (SalesOrderDetail orderDetail : orderDetails) {
            // 查询走货对照库存信息
            List<SalesOrderDetailStock> detailStocks = salesOrderDetailStockService.selectByDetailId(orderDetail.getId());
            
            // 遍历库存记录，提取代办人信息
            for (SalesOrderDetailStock detailStock : detailStocks) {
                // 根据库存ID查询库存信息，获取代办人信息
                Stock stock = stockService.selectById(detailStock.getStockId());
                
                DeductionDetailVO deductionDetail = new DeductionDetailVO();
                deductionDetail.setCommodityId(orderDetail.getCommodityId());
                deductionDetail.setCommodityName(orderDetail.getCommodityName());
                deductionDetail.setSpecsId(orderDetail.getSpecsId());
                deductionDetail.setSpecsName(orderDetail.getSpecsName());
                // 假设扣款金额按库存记录分摊
                deductionDetail.setDeductionAmount(BigDecimal.ZERO);
                deductionDetail.setSalesOutId(salesOutId);
                deductionDetail.setSalesOutDocNo(salesOut.getDocNo());
                // 设置代办人信息
                if (stock != null) {
                    deductionDetail.setAgentName(stock.getAgentName());
                    deductionDetail.setOrderTime(stock.getOrderTime());
                    deductionDetail.setStorageOrderNo(stock.getStorageOrderNo());
                }
                deductionDetails.add(deductionDetail);
            }
        }
        
        result.setAgentDeductions(deductionDetails);
        result.setTotalDeductionAmount(totalDeductionAmount);
        result.setCompanyDeductionAmount(BigDecimal.ZERO);
        
        return result;
    }

    @Override
    public List<AllocationCollectionDetailWithSalesInfo> findByOrderId(Long id) {
        // 原实现只是简单查询AllocationCollectionDetail表
        // QueryWrapper<AllocationCollectionDetail> queryWrapper = new QueryWrapper<>();
        // queryWrapper.eq("sales_out_id", id);
        // return allocationCollectionDetailMapper.selectList(queryWrapper);
        
        // 新实现：结合AllocationCollection和AllocationCollectionDetail两张表，
        // 根据salesOutId查询并返回指定出库单的所有回款信息
        
        // 通过sales_out_id关联查询AllocationCollectionDetail和AllocationCollection表
        return baseMapper.selectCollectionDetailWithSalesInfoBySalesOutId(id);
    }
}